package org.xm.sk.service.data.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xm.sk.dao.UserRepository;
import org.xm.sk.vo.ob.PageConf;
import org.xm.sk.vo.tb.Activity;
import org.xm.sk.vo.tb.Coupon;
import org.xm.sk.vo.tb.Hairdresser;
import org.xm.sk.vo.tb.Role;
import org.xm.sk.vo.tb.User;

@Service
@Transactional
public class UserServiceImpl extends BaseServiceImpl<User> {

    private static final String CACHE_KEY = "'user'";
    private static final String sk_CACHE_NAME = "users";

    @Autowired
    private UserRepository userRepository;

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public User findById(int id) {
        return this.userRepository.getOne(id);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Set<User> findByName(String name) {
        return this.userRepository.findByName(name);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_all'")
    public List<User> findAll() {
        return this.userRepository.findAll();
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public List<User> findAll(User user) {
        ExampleMatcher matcher = ExampleMatcher.matching();
        Example<User> ex = Example.of(user, matcher);
        return this.userRepository.findAll(ex);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Object findAllByPage(PageConf pageConf) {
        List<Order> orders = new ArrayList<Order>();
        if (null != pageConf.getSortNames()) {
            for (int i = 0; i < pageConf.getSortNames().size(); i++) {
                orders.add(new Order(pageConf.getSortOrders().get(i),
                        pageConf.getSortNames().get(i)));
            }
        } else {
            orders.add(new Order(Direction.DESC, "id"));
        }
        Sort sort = Sort.by(orders);
        Pageable pageable = null;
        Specification<User> querySpecifi = new Specification<User>() {

            private static final long serialVersionUID = -7945559545843188173L;

            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                List<Predicate> predicatesAnd = new ArrayList<>();
                predicatesAnd.add(cb
                        .equal(root.get("datalevel").as(Integer.class), 0));
                Map<String, Object> dataMap = pageConf.getDataMap();
                if (null != dataMap && dataMap.containsKey("hairdresserId")
                        && null != dataMap.get("hairdresserId")
                        && 0 < Integer.parseInt(dataMap.get("hairdresserId").toString())) {
                    Integer hairdresserId = Integer.parseInt(dataMap.get("hairdresserId").toString());
                    Join<User, Hairdresser> joinAtHairdresser = root.join("atHairdressers",
                            JoinType.LEFT);
                    predicatesAnd.add(cb.equal(joinAtHairdresser.get("id").as(Integer.class), hairdresserId));
                }
                // 通过优惠活动ID过滤用户，如果用户有此优惠活动的优惠券就不列出
                // 需要三级过滤，从coupons到activity
                if (null != dataMap && dataMap.containsKey("activityId")
                        && null != dataMap.get("activityId")
                        && 0 < Integer.parseInt(dataMap.get("activityId").toString())) {
                    Integer activityId = Integer.parseInt(dataMap.get("activityId").toString());
                    Join<User, Coupon> joinCoupon = root.join("coupons",
                            JoinType.LEFT);
                    Join<Coupon, Activity> joinActivity = joinCoupon.join("activity",
                            JoinType.LEFT);
                    predicatesAnd.add(cb.notEqual(joinActivity.get("id").as(Integer.class), activityId));
                }
                if (null != dataMap && dataMap.containsKey("roleName")
                        && null != dataMap.get("roleName")
                        && 0 < dataMap.get("roleName").toString().trim().length()) {
                    Join<User, Role> joinRole = root.join("roles",
                            JoinType.LEFT);
                    predicatesAnd.add(cb.equal(
                            joinRole.get("name").as(String.class),
                            dataMap.get("roleName")));
                }
                List<Predicate> predicatesOr = new ArrayList<>();
                if (null != pageConf.getSearch()
                        && 0 < pageConf.getSearch().trim().length()) {
                    String search = pageConf.getSearch().trim();
                    // 模糊查找
                    predicatesOr.add(cb.like(root.get("name").as(String.class),
                            "%" + search + "%"));
                    predicatesOr.add(
                            cb.like(root.get("nickname").as(String.class),
                                    "%" + search + "%"));
                    predicatesOr.add(cb.like(root.get("phone").as(String.class),
                            "%" + search + "%"));
                    predicatesOr.add(
                            cb.like(root.get("modifydate").as(String.class),
                                    "%" + search + "%"));
                    Join<Coupon, Role> joinRoles = root.join("roles",
                            JoinType.LEFT);
                    predicatesOr
                            .add(cb.like(joinRoles.get("dame").as(String.class),
                                    "%" + search + "%"));
                }
                if (predicatesOr.isEmpty())
                    return cb.and(predicatesAnd
                                    .toArray(new Predicate[predicatesAnd.size()]));
                return cb.and(
                        cb.or(predicatesOr
                                .toArray(new Predicate[predicatesOr.size()])),
                        cb.and(predicatesAnd
                                .toArray(new Predicate[predicatesAnd.size()])));
            }
        };
        if (null != pageConf.getPage() && null != pageConf.getSize())
            pageable = PageRequest.of(pageConf.getPage(), pageConf.getSize(),
                    sort);
        if (null != pageable)
            return this.userRepository.findAll(querySpecifi, pageable);
        else
            return this.userRepository.findAll(querySpecifi, sort);
    }

    @SuppressWarnings("unchecked")
    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0+'_'+#p1")
    public Object findAllByPage(PageConf pageConf, Object object) {
        Pageable pageable = null;
        if (null != pageConf) {
            pageable = PageRequest.of(
                    -1 < pageConf.getPage() ? pageConf.getPage() : 0,
                    0 < pageConf.getSize() ? pageConf.getSize() : 100);
        }
        if (null != object && object instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) object;
            if (null != map.get("hairdresserid")) {
                Integer hairdresserid = Integer
                        .valueOf(map.get("hairdresserid").toString());
                return userRepository.atList(pageable, hairdresserid);
            } else if (null != map.get("isSignUserId")) {
                Integer userId = Integer
                        .valueOf(map.get("isSignUserId").toString());
                Calendar cal = Calendar.getInstance();
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.MILLISECOND, 0);
                return userRepository.isSign(cal.getTime(), userId);
            }
        }
        return null;
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    // @CachePut(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0.id")
    public User update(User user) {
        user.setEditer(user().getUserid());
        user.setEditerName(user().getUsername());
        user.setModifydate(new Date());
        return this.userRepository.save(user);
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public void delete(int id) {
        this.userRepository.delete(id, user().getUserid(),
                user().getUsername());
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public void delete(Set<Integer> ids) {
        this.userRepository.delete(ids, user().getUserid(),
                user().getUsername());
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public User add(User user) {
        user.setCreator(user().getUserid());
        user.setEditer(user().getUserid());
        user.setCreatorName(user().getUsername());
        user.setEditerName(user().getUsername());
        return this.userRepository.saveAndFlush(user);
    }

}
